package com.company.common.util;

import com.alibaba.fastjson.JSONObject;
import com.company.common.enums.RedisKeyEnum;
import org.apache.commons.lang3.ArrayUtils;
import redis.clients.jedis.Jedis;

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * Description: cdndownLoad
 * Created by hekai on 2021/4/19 9:56
 */
public class RSAUtil {

    private static final String KEY_ALGORITHM = "RSA";

    private static final char[] bcdLookup = { '0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

    public static void main(String[] args) {
        try {
            genKey();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Map<String, String> genKey() throws Exception {
        Map<String, String> returnMap = new HashMap<String, String>();
        KeyPairGenerator keygen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        SecureRandom random = new SecureRandom();
        keygen.initialize(1024, random);
        // 取得密钥对
        KeyPair kp = keygen.generateKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) kp.getPrivate();
        String privateKeyString = bytesToHexStr(privateKey.getEncoded());

        RSAPublicKey publicKey = (RSAPublicKey) kp.getPublic();
        String publicKeyString = bytesToHexStr(publicKey.getEncoded());
        returnMap.put(RedisKeyEnum.RSA_PUBLIC_KEY.getKey(), publicKeyString);
        returnMap.put(RedisKeyEnum.RSA_PRIVATE_KEY.getKey(), privateKeyString);

        System.out.println("PUBLIC_KEY length:" + publicKeyString.length()
                + "\r\nPUBLIC_KEY:" + publicKeyString);
        System.out.println("PRIVATE_KEY length:" + privateKeyString.length()
                + "\r\nPRIVATE_KEY:" + privateKeyString);
        Jedis jedis = new Jedis("localhost",6379);
        jedis.hset(RedisKeyEnum.RSA_HASH_KEY.getKey(),returnMap);
        return returnMap;
    }

    public static RSAPublicKey getPublicKey(String publicKey) throws Exception {
        byte[] keyBytes = hexStrToBytes(publicKey);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return (RSAPublicKey) keyFactory.generatePublic(spec);
    }

    public static RSAPrivateKey getPrivateKey(String privateKey)
            throws Exception {
        byte[] keyBytes = hexStrToBytes(privateKey);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return (RSAPrivateKey) keyFactory.generatePrivate(spec);
    }

    public static String encrypt(Map<String, String> params, String publicKey)
            throws Exception {
        JSONObject object = new JSONObject();
        object.putAll(params);
        return encrypt(object.toString(), publicKey);
    }

    public static String encrypt(String info, String publicKey)
            throws Exception {
        RSAPublicKey publickey = getPublicKey(publicKey);
        byte[] bytes = encrypt(info.getBytes("utf-8"), publickey);
        return bytesToHexStr(bytes);
    }

    private static byte[] encrypt(byte[] text, RSAPublicKey pubRSA)
            throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, pubRSA);
        byte[] dataReturn = new byte[0];
        for (int i = 0; i < text.length; i += 245) {
            byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(text, i,
                    i + 245));
            dataReturn = ArrayUtils.addAll(dataReturn, doFinal);
        }
        return dataReturn;
    }

    public static String decrypt(String sign, String privateKey)
            throws Exception {
        RSAPrivateKey privatekey = getPrivateKey(privateKey);
        byte[] bytes = decrypt((hexStrToBytes(sign)), privatekey);
        return new String(bytes, "utf-8");
    }

    private static byte[] decrypt(byte[] src, RSAPrivateKey prK)
            throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, prK);
        byte[] dataReturn = new byte[0];
        for (int i = 0; i < src.length; i += 256) {
            byte[] doFinal = cipher.doFinal(ArrayUtils
                    .subarray(src, i, i + 256));
            dataReturn = ArrayUtils.addAll(dataReturn, doFinal);
        }
        return dataReturn;
    }

    /**
     * Transform the specified byte into a Hex String form.
     */
    private static final String bytesToHexStr(byte[] bcd) {
        StringBuffer s = new StringBuffer(bcd.length * 2);
        for (int i = 0; i < bcd.length; i++) {
            s.append(bcdLookup[(bcd[i] >>> 4) & 0x0f]);
            s.append(bcdLookup[bcd[i] & 0x0f]);
        }
        return s.toString();
    }

    /**
     * Transform the specified Hex String into a byte array.
     */
    private static final byte[] hexStrToBytes(String s) {
        byte[] bytes;
        bytes = new byte[s.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) Integer.parseInt(s.substring(2 * i, 2 * i + 2),
                    16);
        }
        return bytes;
    }

}